package top.juc.plugin.idea.mavenversionuploadx.dialog;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.ui.JBUI;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.GenericDomValue;
import com.intellij.util.xml.reflect.AbstractDomChildrenDescription;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.dom.model.MavenDomDependencies;
import org.jetbrains.idea.maven.dom.model.MavenDomDependency;
import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;
import org.jetbrains.idea.maven.dom.model.MavenDomProperties;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.utils.actions.MavenActionUtil;
import top.juc.plugin.idea.mavenversionuploadx.util.VersionUtil;

import javax.swing.*;
import java.util.*;

/**
 * @author Felix
 * @date 2020/8/18 15:53
 */
public class MvnProjectVersionDialog extends DialogWrapper {

    private final Project project;

    private final MavenProjectsManager projectsManager;

    private final List<MavenProject> allProjects;

    private final List<MavenProject> rootProjects;

    private MavenProject rootProject;

    private String rootProjectGroupIdKey;

    private String rootProjectArtifactIdKey;

    private JTextField newVersionContent;

    private Set<String> dependencyKeySet = new HashSet<>();

    public MvnProjectVersionDialog(AnActionEvent e) {
        super(e.getProject());
        setTitle("Set the Project Version");
        this.project = e.getProject();
        projectsManager = MavenActionUtil.getProjectsManager(e.getDataContext());
        allProjects = projectsManager == null ? new ArrayList<>() : projectsManager.getProjects();
        rootProjects = projectsManager == null ? new ArrayList<>() : projectsManager.getRootProjects();
        init();
    }

    @Override
    protected @Nullable JComponent createCenterPanel() {
        JPanel dialogPanel = new JPanel();
        dialogPanel.setLayout(new GridLayoutManager(2, 2, JBUI.emptyInsets(), -1, -1));
        JLabel label = new JLabel("Root Project:");
        dialogPanel.add(label, new GridConstraints(0, 0, 1, 1,
                GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                null, null, null));
        ComboBox<String> comboBox = new ComboBox<>();
        // 设置为可编辑
        comboBox.setEditable(true);
        comboBox.setRenderer(new CustomRenderer());
        dialogPanel.add(comboBox, new GridConstraints(0, 1, 1, 1,
                GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                null, null, null));


        JLabel newVersion = new JLabel("New Version:");
        newVersionContent = new JTextField();
        dialogPanel.add(newVersion, new GridConstraints(1, 0, 1, 1,
                GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                null, null, null));
        dialogPanel.add(newVersionContent, new GridConstraints(1, 1, 1, 1,
                GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                null, null, null));
        flushComboBox(comboBox);
        comboBox.addActionListener(e -> {
            String o = (String)comboBox.getSelectedItem();
            if (StringUtils.isNoneBlank(o)) {
                String[] split = StringUtils.split(o, ":");
                if (split == null || split.length < 2) {
                    return;
                }
                rootProjectGroupIdKey = split[0];
                rootProjectArtifactIdKey = split[1];
                rootProject = allProjects.stream().filter(this::ifProject).max(Comparator.comparing(f -> Optional.of(f).map(v -> v.getMavenId().getVersion()).orElse("1.0.0"))).orElseGet(() -> rootProjects.get(0));
                initVersionField();
            }
        });
        SwingUtilities.invokeLater(() -> newVersionContent.requestFocusInWindow());
        return dialogPanel;
    }

    @Override
    protected JComponent createSouthPanel() {
        JPanel south = new JPanel();
        JButton submit = new JButton("Submit");
        submit.addActionListener(e -> WriteCommandAction.runWriteCommandAction(project, this::updateVersion));
        submit.setHorizontalAlignment(SwingConstants.CENTER);
        submit.setVerticalAlignment(SwingConstants.CENTER);
        south.add(submit);
        return south;
    }

    private void initVersionField() {
        VirtualFile file = rootProject.getFile();
        PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
        DomManager domManager = DomManager.getDomManager(project);
        DomFileElement<MavenDomProjectModel> fileElement = domManager.getFileElement((XmlFile) psiFile, MavenDomProjectModel.class);
        if (fileElement != null) {
            MavenDomProjectModel rootElement = fileElement.getRootElement();

            GenericDomValue<String> version = rootElement.getVersion();
            if(ObjectUtils.isNotEmpty(version.getValue())){
                newVersionContent.setText(this.upgradeVersion(version.getValue()));
            }else{
                GenericDomValue<String> parentVersion = rootElement.getMavenParent().getVersion();
                if(ObjectUtils.isNotEmpty(parentVersion.getValue())){
                    newVersionContent.setText(this.upgradeVersion(parentVersion.getValue()));
                }
            }
        }
    }

    private String upgradeVersion(String version){
        try{
           return VersionUtil.upgradeVersion(version);
        }catch (Exception e){
            return version;
        }
    }

    private void flushComboBox(ComboBox<String> comboBox) {
        for (MavenProject mavenProject : rootProjects) {
            comboBox.addItem(mavenProject.getMavenId().getGroupId() + ":" + mavenProject.getMavenId().getArtifactId());
        }
        if (!rootProjects.isEmpty()) {
            rootProject = rootProjects.get(0);
            rootProjectGroupIdKey = rootProject.getMavenId().getGroupId();
            rootProjectArtifactIdKey = rootProject.getMavenId().getArtifactId();
            initVersionField();
        }
    }

    private void updateVersion() {
        final String newVersion = newVersionContent.getText();
        DomManager domManager = DomManager.getDomManager(project);
        try {
            if (!newVersion.isEmpty()) {
                for (MavenProject mavenProject : allProjects) {
                    VirtualFile file = mavenProject.getFile();
                    PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
                    XmlFile xmlFile = (XmlFile) psiFile;
                    if (xmlFile != null) {
                        updateModuleVersion(newVersion, xmlFile);
                    }
                    DomFileElement<MavenDomProjectModel> fileElement = domManager.getFileElement(xmlFile, MavenDomProjectModel.class);
                    if (fileElement != null) {
                        MavenDomProjectModel rootElement = fileElement.getRootElement();
                        updateDependencyVersion(rootElement, newVersion);
                    }
                }
                try {
                    FileDocumentManager.getInstance().saveAllDocuments();
                    projectsManager.forceUpdateProjects(allProjects);
                } catch (Exception e) {
                    FileDocumentManager.getInstance().saveAllDocuments();
                    projectsManager.forceUpdateProjects(allProjects);
                }
            }
        } catch (Exception ignored) {

        }
        this.close(DialogWrapper.OK_EXIT_CODE);
    }

    private Collection<String> getAllModuleNames(){
        Set<String> result = new HashSet<>();
        for (MavenProject mavenProject : allProjects) {
            result.addAll(mavenProject.getModulesPathsAndNames().values());
        }
        return result;
    }

    private void updateDependencyVersion(MavenDomProjectModel mavenDomProjectModel, String newVersion) {
        updateDependencyVersion(mavenDomProjectModel.getDependencies(), newVersion);
        updateDependencyVersion(mavenDomProjectModel.getDependencyManagement().getDependencies(), newVersion);
        updatePropertiesVersion(mavenDomProjectModel, newVersion);
    }

    private void updateDependencyVersion(MavenDomDependencies dependencies, String newVersion) {
        List<MavenDomDependency> dependencyList = dependencies.getDependencies();
        for (MavenDomDependency dependency : dependencyList) {
            if (this.ifProject(dependency) && this.ifVersion(dependency.getVersion())) {
                dependency.getVersion().setValue(newVersion);
            }

        }
    }

    private void updatePropertiesVersion(MavenDomProjectModel mavenDomProjectModel, String newVersion) {
        XmlTag propertiesTag = mavenDomProjectModel.getProperties().getXmlTag();
        if (propertiesTag == null) {
            return;
        }

        // 遍历 properties
        for (XmlTag propertyTag : propertiesTag.getSubTags()) {
            String key = propertyTag.getName();
            if(dependencyKeySet.contains(key)){
                if(key.equals("revision") && !ifProject(mavenDomProjectModel)){
                    continue;
                }
                propertyTag.getValue().setText(newVersion);
            }

            String value = propertyTag.getValue().getTrimmedText();
//            System.out.println(key + " = " + value);


        }

//        List<MavenDomDependency> dependencyList = dependencies.getDependencies();
//        for (MavenDomDependency dependency : dependencyList) {
//            if (this.ifProject(dependency) && this.ifVersion(dependency.getVersion())) {
//                dependency.getVersion().setValue(newVersion);
//            }
//
//        }
    }

    private void updateModuleVersion(String newVersion, XmlFile xmlFile) {
        final XmlTag rootTag = xmlFile.getRootTag();
        if (rootTag != null) {
            XmlTag parentTag = rootTag.findFirstSubTag("parent");
            if (parentTag != null) {
                boolean projectFromParent = ifProject(parentTag);

                XmlTag versionTagFromParent = parentTag.findFirstSubTag("version");
                if (projectFromParent && ifVersion(versionTagFromParent)) {
                    versionTagFromParent.getValue().setText(newVersion);
                }
            }
            boolean projectFromParent = ifProject(rootTag);
            XmlTag versionTag = rootTag.findFirstSubTag("version");
            if (projectFromParent && ifVersion(versionTag)) {
                versionTag.getValue().setText(newVersion);
            }
        }
    }



    private boolean ifProject(MavenDomDependency dependency) {
        boolean ifGroupId = Objects.equals(dependency.getGroupId().getRawText(), rootProjectGroupIdKey);
        boolean ifArtifactId = StringUtils.contains(dependency.getArtifactId().getRawText(),rootProjectArtifactIdKey);
        if(StringUtils.isNoneBlank(dependency.getGroupId().getRawText())){
            return ifGroupId && ifArtifactId;
        }else{
            return ifArtifactId;
        }
    }

    private boolean ifProject(XmlTag tag) {
        if (tag != null) {
            XmlTag groupIdTagFromParent = tag.findFirstSubTag("groupId");
            XmlTag artifactIdTagFromParent = tag.findFirstSubTag("artifactId");
            boolean ifGroupId = Objects.nonNull(groupIdTagFromParent)
                && Objects.equals(groupIdTagFromParent.getValue().getText(), rootProjectGroupIdKey);
            boolean ifArtifactId = Objects.nonNull(artifactIdTagFromParent)
                && StringUtils.contains(artifactIdTagFromParent.getValue().getText(),rootProjectArtifactIdKey);
            if(Objects.nonNull(groupIdTagFromParent)  && StringUtils.isNoneBlank(groupIdTagFromParent.getValue().getText())){
                return ifGroupId && ifArtifactId;
            }else{
                return ifArtifactId;
            }
        }
        return false;
    }

    private boolean ifProject(MavenProject project){
        boolean ifGroupId = Objects.equals(project.getMavenId().getGroupId(), rootProjectGroupIdKey);
        boolean ifArtifactId = StringUtils.contains(project.getMavenId().getArtifactId(), rootProjectArtifactIdKey);
        if(StringUtils.isNoneBlank(project.getMavenId().getGroupId())){
            return ifGroupId && ifArtifactId;
        }else{
            return ifArtifactId;
        }
    }


    private boolean ifProject(MavenDomProjectModel element){
        boolean ifGroupId = Objects.equals(element.getGroupId().getRawText(), rootProjectGroupIdKey);
        boolean ifArtifactId = StringUtils.contains(element.getArtifactId().getRawText(), rootProjectArtifactIdKey);
        if(StringUtils.isNoneBlank(element.getGroupId().getRawText())){
            return ifGroupId && ifArtifactId;
        }else{
            return ifArtifactId;
        }
    }

    /**
     * 是否为变量
     * @param versionTag
     * @return
     */
    private boolean ifVersion(XmlTag versionTag){
        if(versionTag == null){
            return false;
        }
        if(StringUtils.startsWith(versionTag.getValue().getText(),"${")){
            this.addDependencyKeySet(versionTag.getValue().getText());
            return false;
        }
        return true;
    }

    public boolean ifVersion(GenericDomValue<String> version) {
        if (version == null) {
            return false;
        }
        if (StringUtils.startsWith(version.getRawText(), "${")) {
            this.addDependencyKeySet(version.getRawText());
            return false;
        }
        return true;
    }


    public void addDependencyKeySet(String value){
        if(StringUtils.startsWith(value,"${")){
            String start = StringUtils.removeStart(value, "${");
            String result = StringUtils.removeEnd(start, "}");
            dependencyKeySet.add(result);
        }
    }
}
